﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content.Pipeline;
using TiledLib;

namespace MegavaniaX.ContentImporter
{
    /*[ContentSerializerRuntimeType("MegavaniaX.World.ActorInfo, MegavaniaX")]
    public class MegavaniaActorInfoContent
    {
        public string ActorType;
        public string ActorID;
    }*/

    [ContentSerializerRuntimeType("MegavaniaX.World.Tile, MegavaniaX")]
    public class MegavaniaTileContent
    {
        // public ExternalReference<Texture2DContent> Texture;
        // public ExternalReference<Texture2DContent>[] Textures;
        // public Rectangle SourceRectangle;
        public MegavaniaTileTextureContent Textures;
        public int X;
        public int Y;
        public bool IsSolid;
        public bool IsLadder;
    }

    [ContentSerializerRuntimeType("MegavaniaX.World.Screen, MegavaniaX")]
    public class MegavaniaScreenContent
    {
        public int Width;
        public int Height;
        public int OffsetX;
        public int OffsetY;
        public string ScreenID;

        public MegavaniaTileContent[] Tiles;
    }

    [ContentSerializerRuntimeType("MegavaniaX.World.TileTexture, MegavaniaX")]
    public class MegavaniaTileTextureContent
    {
        public ExternalReference<Texture2DContent> Texture;
        public Rectangle[] SourceRectangles;
    }

    [ContentSerializerRuntimeType("MegavaniaX.World.Map, MegavaniaX")]
    public class MegavaniaMapContent
    {
        public IList<MegavaniaScreenContent> Screens = new List<MegavaniaScreenContent>();
        //public IList<MegavaniaActorInfoContent> Actors = new List<MegavaniaActorInfoContent>();
    }

    [ContentProcessor(DisplayName = "Megavania TMX Processor")]
    public class MegavaniaMapProcessor : ContentProcessor<MapContent, MegavaniaMapContent>
    {
        public override MegavaniaMapContent Process(MapContent input, ContentProcessorContext context)
        {
            TiledHelpers.BuildTileSetTextures(input, context);

            // generate source rectangles
            TiledHelpers.GenerateTileSourceRectangles(input);


            var output = new MegavaniaMapContent();
            var tlc = input.Layers.Single(x => x.Name == "tiles") as TileLayerContent;

            // Step one: generate a list of screens based on the maps "screenlist" property.
            string[] screenNames = input.Properties["screenlist"].Split(',');
            foreach (string screenName in screenNames)
            {
                // SCREENPROPS FORMAT: offsetX, offsetY, width, height
                string[] screenProps = input.Properties[screenName].Split(',');
                var screen = new MegavaniaScreenContent()
                {
                    Width = Int32.Parse(screenProps[2]),
                    Height = Int32.Parse(screenProps[3]),
                    OffsetX = Int32.Parse(screenProps[0]),
                    OffsetY = Int32.Parse(screenProps[1]),                   
                };
                screen.Tiles = new MegavaniaTileContent[screen.Height * 16 * screen.Width * 16];


                // now add the appropriate tiles for this screen
                int startx = 16 * screen.OffsetX;
                int xmax = (16 * screen.Width) + 16 * screen.OffsetX;
                int starty = 16 * screen.OffsetY;
                int ymax = (16 * screen.Height) + 16 * screen.OffsetY;

                int currX = startx;
                int currY = starty;                
                // System.Diagnostics.Debugger.Launch();
                while (currX < xmax && currY < ymax)
                {

                    // add tile
                    uint tileID = tlc.Data[GetArrayPosition(input, currX, currY)];

                    // use that to get the actual index as well as the SpriteEffects
                    int tileIndex;
                    bool isSolid = false;
                    bool isLadder = false;
                    TiledHelpers.DecodeTileID(tileID, out tileIndex);

                    // figure out which tile set has this tile index in it and grab
                    // the texture reference and source rectangle.
                    Rectangle[] sourceRects = null;
                    MegavaniaTileTextureContent ttc = new MegavaniaTileTextureContent();
                    // iterate all the tile sets
                    foreach (var tileSet in input.TileSets)
                    {
                        // if our tile index is in this set
                        if (tileIndex - tileSet.FirstId >= 0 && tileIndex - tileSet.FirstId < tileSet.Tiles.Count)
                        {
                            int arrayIdx = (int)(tileIndex - tileSet.FirstId);

                            // store the texture content and source rectangle
                            ttc.Texture = tileSet.Texture;
                            if (tileSet.Tiles[arrayIdx].Properties.ContainsKey("solid"))
                                isSolid = tileSet.Tiles[arrayIdx].Properties["solid"] == "true";
                            if (tileSet.Tiles[arrayIdx].Properties.ContainsKey("ladder"))
                                isLadder = tileSet.Tiles[arrayIdx].Properties["ladder"] == "true";

                            // if the tile is animated, we need to do some more digging to find the associated textures.  not the most efficient way but this is done at compile time so w/e
                            if (tileSet.Tiles[arrayIdx].Properties.ContainsKey("animationgroup"))
                            {
                                CreateSourceRects(tileSet, tileSet.Tiles[arrayIdx], out sourceRects);
                                ttc.SourceRectangles = sourceRects;
                            }
                            else // non-animated case
                            {
                                sourceRects = new Rectangle[1] { new Rectangle() };
                                sourceRects[0] = tileSet.Tiles[arrayIdx].Source;                               
                                ttc.SourceRectangles = sourceRects;
                            }

                            // and break out of the foreach loop
                            break;
                        }
                    }

                    // now insert the tile into our output
                    // convert global x coord into local x coord
                    int localX = currX - (screen.OffsetX * 16);
                    int localY = currY - (screen.OffsetY * 16);


                    screen.Tiles[screen.Width * 16 * localY + localX] = new MegavaniaTileContent
                    {                        
                        Textures = ttc,
                        IsSolid = isSolid,
                        IsLadder = isLadder,
                        X = localX,
                        Y = localY,
                        //Coordinates = new Vector2(gx * 16.0f, gy * 16.0f)
                    };


                    currX++;
                    if (currX >= xmax) // next row
                    {
                        currX = startx;
                        currY++;
                    }
                   
                }

                output.Screens.Add(screen);
            }
             /*                       

            int gx = 0, gy = 0;

            // temp: just one screen
            var screen = new MegavaniaScreenContent()
            {
                Width = 2,
                Height = 1,
                OffsetX = 0,
                OffsetY = 0,
            };

            // tile layer processing
            var tlc = input.Layers.Single(x => x.Name == "tiles") as TileLayerContent;
            //System.Diagnostics.Debugger.Launch();
            screen.Tiles = new MegavaniaTileContent[tlc.Data.Length];
            for (int i = 0; i < tlc.Data.Length; i++)
            {


                uint tileID = tlc.Data[i];

                // use that to get the actual index as well as the SpriteEffects
                int tileIndex;
                bool isSolid = false;
                TiledHelpers.DecodeTileID(tileID, out tileIndex);

                // figure out which tile set has this tile index in it and grab
                // the texture reference and source rectangle.
                ExternalReference<Texture2DContent> textureContent = null;
                Rectangle sourceRect = new Rectangle();

                // iterate all the tile sets
                foreach (var tileSet in input.TileSets)
                {
                    // if our tile index is in this set
                    if (tileIndex - tileSet.FirstId >= 0 && tileIndex - tileSet.FirstId < tileSet.Tiles.Count)
                    {
                        // store the texture content and source rectangle
                        textureContent = tileSet.Texture;
                        sourceRect = tileSet.Tiles[(int)(tileIndex - tileSet.FirstId)].Source;
                        if (tileSet.Tiles[(int)(tileIndex - tileSet.FirstId)].Properties.ContainsKey("solid"))
                            isSolid = tileSet.Tiles[(int)(tileIndex - tileSet.FirstId)].Properties["solid"] == "true";
                        // and break out of the foreach loop
                        break;
                    }
                }


                // now insert the tile into our output
                screen.Tiles[i] = new MegavaniaTileContent
                {
                    Texture = textureContent,
                    SourceRectangle = sourceRect,
                    IsSolid = isSolid,
                    X = gx,
                    Y = gy,
                    //Coordinates = new Vector2(gx * 16.0f, gy * 16.0f)
                };

                gx++;
                if (gx > 31)
                {
                    gx = 0;
                    gy++;
                }

            }
            // add the layer to our output
            output.Screens.Add(screen);
            */
            // return the output object. because we have ContentSerializerRuntimeType attributes on our
            // objects, we don't need a ContentTypeWriter and can just use the automatic serialization.
            return output;
        }

        private IDictionary<string, Rectangle[]> calculatedAnimations = new Dictionary<string, Rectangle[]>();

        private void CreateSourceRects(TileSetContent tileSet, Tile initialTile, out Rectangle[] sourceRects)
        {
            string animGroup = initialTile.Properties["animationgroup"];
            // check if this has been calculated
            if (calculatedAnimations.ContainsKey(animGroup))
            {
                sourceRects = calculatedAnimations[animGroup];
            }
            else
            {
                // get all tiles in the tileset in the same animation group
                var allTiles = tileSet.Tiles.Where(x => x.Properties.ContainsKey("animationgroup") && x.Properties["animationgroup"] == animGroup);
                int count = allTiles.Count();
                if (count == 0) throw new InvalidOperationException("Could not find animation group " + animGroup);
                sourceRects = new Rectangle[count];
                for (int i = 0; i < count; i++)
                {
                    var tile = allTiles.Single(x => x.Properties["animationorder"] == (i + 1).ToString());
                    sourceRects[i] = tile.Source;
                }
                calculatedAnimations[animGroup] = sourceRects;
            }
        }

        private static int GetArrayPosition(MapContent input, int x, int y)
        {
            return (input.Width * y) + x;
        }


    }
}
